#region Namespaces

using System.IO;
using System.Xml;
using System.Xml.XPath;

using Vibstudio.X4NET.Xml.Linq;
using Vibstudio.X4NET.Xml.XPath;

#endregion

namespace Vibstudio.X4NET.Xml.Navigation
{
    public class XmlNavigator
    {
        public virtual XmlKeyValuePairCollection GetKeyValuePairs(string uri)
        {
            return GetKeyValuePairs(uri,
                                    string.Empty,
                                    string.Empty,
                                    Defaults.XPathRoot,
                                    true,
                                    true,
                                    SearchCriterion.None,
                                    string.Empty);
        }

        public virtual XmlKeyValuePairCollection GetKeyValuePairs(string uri, bool includingEmptyValues)
        {
            return GetKeyValuePairs(uri,
                                    string.Empty,
                                    string.Empty,
                                    Defaults.XPathRoot,
                                    includingEmptyValues,
                                    true,
                                    SearchCriterion.None,
                                    string.Empty);
        }

        public virtual XmlKeyValuePairCollection GetKeyValuePairs(string uri, bool includingEmptyValues, bool includingAttributes)
        {
            return GetKeyValuePairs(uri,
                                    string.Empty,
                                    string.Empty,
                                    Defaults.XPathRoot,
                                    includingEmptyValues,
                                    includingAttributes,
                                    SearchCriterion.None,
                                    string.Empty);
        }

        public virtual XmlKeyValuePairCollection GetKeyValuePairs(string uri, bool includingEmptyValues, bool includingAttributes, bool positional, bool textual)
        {
            return GetKeyValuePairs(uri,
                                    string.Empty,
                                    string.Empty,
                                    Defaults.XPathRoot,
                                    includingEmptyValues,
                                    includingAttributes,
                                    SearchCriterion.None,
                                    string.Empty,
                                    positional,
                                    textual);
        }

        public virtual XmlKeyValuePairCollection GetKeyValuePairs(string uri, string xpath)
        {
            return GetKeyValuePairs(uri,
                                    string.Empty,
                                    string.Empty,
                                    xpath,
                                    true,
                                    true,
                                    SearchCriterion.None,
                                    string.Empty);
        }

        public virtual XmlKeyValuePairCollection GetKeyValuePairs(string uri, string xpath, bool includingEmptyValues)
        {
            return GetKeyValuePairs(uri,
                                    string.Empty,
                                    string.Empty,
                                    xpath,
                                    includingEmptyValues,
                                    true,
                                    SearchCriterion.None,
                                    string.Empty);
        }

        public virtual XmlKeyValuePairCollection GetKeyValuePairs(string uri, SearchCriterion criteria, string searchValue)
        {
            return GetKeyValuePairs(uri,
                                    string.Empty,
                                    string.Empty,
                                    Defaults.XPathRoot,
                                    true,
                                    true,
                                    criteria,
                                    searchValue);
        }

        public virtual XmlKeyValuePairCollection GetKeyValuePairs(string uri, bool includingEmptyValues, SearchCriterion criteria, string searchValue)
        {
            return GetKeyValuePairs(uri,
                                    string.Empty,
                                    string.Empty,
                                    Defaults.XPathRoot,
                                    includingEmptyValues,
                                    true,
                                    criteria,
                                    searchValue);
        }

        public virtual XmlKeyValuePairCollection GetKeyValuePairs(string uri,
                                                                  string namespacePrefix,
                                                                  string namespaceUri,
                                                                  string xpath,
                                                                  bool includingEmptyValues,
                                                                  bool includingAttributes,
                                                                  SearchCriterion criteria,
                                                                  string searchValue,
                                                                  bool positional = true,
                                                                  bool textual = false)
        {
            return GetKeyValuePairs(XDocument.Load(uri),
                                    namespacePrefix,
                                    namespaceUri,
                                    xpath,
                                    includingEmptyValues,
                                    includingAttributes,
                                    criteria,
                                    searchValue,
                                    positional,
                                    textual);
        }

        public virtual XmlKeyValuePairCollection GetKeyValuePairs(XDocument xml,
                                                                  string namespacePrefix,
                                                                  string namespaceUri,
                                                                  string xpath,
                                                                  bool includingEmptyValues,
                                                                  bool includingAttributes,
                                                                  SearchCriterion criteria,
                                                                  string searchValue,
                                                                  bool positional = true,
                                                                  bool textual = false)
        {
            XmlKeyValuePairCollection keyValuePairs = new XmlKeyValuePairCollection { XPaths = xml.ExtractXPaths(positional, textual, includingAttributes, xpath) };

            StringsSeeker seeker = new StringsSeeker(criteria);

            XmlNamespaceManager namespaceManager = new XmlNamespaceManager(new NameTable());
            namespaceManager.AddNamespace(namespacePrefix, namespaceUri);

            using (StringReader reader = new StringReader(xml.ToString()))
            {
                XPathDocument document = new XPathDocument(reader);

                XPathNavigator navigator = ((IXPathNavigable)document).CreateNavigator();

                if (navigator != null)
                {
                    XPathNodeIterator mainIterator = navigator.Select(xpath, namespaceManager);

                    while (mainIterator.MoveNext())
                    {
                        XPathNodeIterator iteratorDescendants =
                            mainIterator.Current.SelectDescendants(XPathNodeType.Element, true);

                        if (includingAttributes)
                        {
                            XPathNavigator navigatorAttributes = iteratorDescendants.Current.Clone();

                            if (navigatorAttributes.MoveToFirstAttribute())
                            {
                                if (!includingEmptyValues)
                                {
                                    if (string.IsNullOrEmpty(navigatorAttributes.Value) == false)
                                    {
                                        if (seeker.IsExpectedString(navigatorAttributes.Value, searchValue))
                                        {
                                            keyValuePairs.Add(new XmlKeyValuePair(navigatorAttributes.Name, navigatorAttributes.Value));
                                        }
                                    }
                                }
                                else
                                {
                                    if (seeker.IsExpectedString(navigatorAttributes.Value, searchValue))
                                    {
                                        keyValuePairs.Add(new XmlKeyValuePair(navigatorAttributes.Name, navigatorAttributes.Value));
                                    }
                                }
                            }

                            while (navigatorAttributes.MoveToNextAttribute())
                            {
                                if (!includingEmptyValues)
                                {
                                    if (string.IsNullOrEmpty(navigatorAttributes.Value) == false)
                                    {
                                        if (seeker.IsExpectedString(navigatorAttributes.Value, searchValue))
                                        {
                                            keyValuePairs.Add(new XmlKeyValuePair(navigatorAttributes.Name, navigatorAttributes.Value));
                                        }
                                    }
                                }
                                else
                                {
                                    if (seeker.IsExpectedString(navigatorAttributes.Value, searchValue))
                                    {
                                        keyValuePairs.Add(new XmlKeyValuePair(navigatorAttributes.Name, navigatorAttributes.Value));
                                    }
                                }
                            }
                        }

                        while (iteratorDescendants.MoveNext())
                        {
                            if (iteratorDescendants.Current.IsSimpleValue())
                            {
                                if (!includingEmptyValues)
                                {
                                    if (string.IsNullOrEmpty(iteratorDescendants.Current.Value) == false)
                                    {
                                        if (seeker.IsExpectedString(iteratorDescendants.Current.Value, searchValue))
                                        {
                                            keyValuePairs.Add(new XmlKeyValuePair(iteratorDescendants.Current.Name, iteratorDescendants.Current.Value));
                                        }
                                    }
                                }
                                else
                                {
                                    if (seeker.IsExpectedString(iteratorDescendants.Current.Value, searchValue))
                                    {
                                        keyValuePairs.Add(new XmlKeyValuePair(iteratorDescendants.Current.Name, iteratorDescendants.Current.Value));
                                    }
                                }
                            }
                        }
                    }
                }
            }

            return keyValuePairs;
        }
    }
}